// todo: selectionchange事件应该需要参数使能知道是谁引起的，用户操作还是内部操作.

function Selection(textView){
    this.init$Eventable();
    this.initRenderable();
    this.$textView = textView;
    this.$textModel = textView.getTextModel();
    this.$mover = new Mover(this.$textModel);
    this.on(this.$textView, 'MouseDown', this._onMouseDown);
    this.on(this.$textModel, 'textchanged', this._onTextChanged);
    this.$focusOffset = 0;
    this.$anchorOffset = 0;
}

Selection.prototype = {
    _TPL_ELM: '<span id="#{id}"></span>',
    getStartOffset: function (){
        return this.$focusOffset < this.$anchorOffset ?
            this.$focusOffset : this.$anchorOffset;
    },
    getEndOffset: function (){
        return this.$focusOffset < this.$anchorOffset ?
            this.$anchorOffset : this.$focusOffset;
    },
    getFocusOffset: function (){
        return this.$focusOffset;
    },
    getAnchorOffset: function (){
        return this.$anchorOffset;
    },
    isCollapsed: function (){
        return this.$focusOffset == this.$anchorOffset;
    },
    collapse: function (toStart){
        this.setOffset(toStart ? this.getStartOffset() : this.getEndOffset());
    },
    setOffset: function (offset){
        this.$focusOffset = offset;
        this.$anchorOffset = offset;
        this._selectionChange();
    },
    setFocusOffset: function (offset){
        this.$focusOffset = offset;
        this._selectionChange();
    },
    setAnchorOffset: function (offset){
        this.$anchorOffset = offset;
        this._selectionChange();
    },
    renderHTML: function (){
        return Utils.format(this._TPL_ELM, { id: this.id() });
    },
    _onMouseDown: function (evt){
        var hitTestInfo = this.$textView.hitTest([evt.clientX, evt.clientY], true);
        this.on(document, 'mousemove', this._onMouseMove);
        this.on(document, 'mouseup', this._onMouseUp);
        if (evt.shiftKey) {
            this.setFocusOffset(hitTestInfo[0]);
        } else {
            this.setOffset(hitTestInfo[0]);
        }
        this.$textView.redraw();
    },
    _onMouseMove: function (evt){
        var hitTestInfo = this.$textView.hitTest([evt.clientX, evt.clientY], true);
        this.setFocusOffset(hitTestInfo[0]);
        this.$textView.redraw();
    },
    _onMouseUp: function (evt){
        var hitTestInfo = this.$textView.hitTest([evt.clientX, evt.clientY], true);
        this.un(document, 'mousemove', this._onMouseMove);
        this.un(document, 'mouseup', this._onMouseUp);
        this.setFocusOffset(hitTestInfo[0]);
        this.$textView.redraw();
    },
    _selectionChange: function (){
        this.dispatchEvent({
            type: 'Changed'
        });
        var textView = this.$textView;
        if (!textView.isRendered()) {
            return;
        }
        var textModel = this.$textModel;
        var buff = [];
        // todo: 将高亮当前行移到插件中去处理. 需要判断焦点情况（和cursor类似）
        var focusLineIndex = textModel.getLineIndex(this.getFocusOffset());
        var focusPosition = textView.getPosition(this.getFocusOffset());
        buff.push('<div class="QEActiveLine" style="left:0;top:', focusPosition[1],
                'px;height:', this.$textView.getLineHeight(focusLineIndex),
                'px"></div>');
        if (!this.isCollapsed()) {
            var startLineIndex = textModel.getLineIndex(this.getStartOffset());
            var endLineIndex = textModel.getLineIndex(this.getEndOffset());
            var startPosition = this.$textView.getPosition(this.getStartOffset());
            var endPosition = this.$textView.getPosition(this.getEndOffset());
            if (startLineIndex === endLineIndex) {
                buff.push('<div class="QESelectionPart" style="left:', startPosition[0],
                    'px;top:', startPosition[1],
                    'px;width:', endPosition[0] - startPosition[0],
                    'px;height:', this.$textView.getLineHeight(startLineIndex),
                    'px"></div>');
            } else {
                buff.push('<div class="QESelectionPart" style="left:', startPosition[0],
                    'px;top:', startPosition[1],
                    'px;width:', textView.getContentWidth() - startPosition[0],
                    'px;height:', this.$textView.getLineHeight(startLineIndex),
                    'px"></div>');
                buff.push('<div class="QESelectionPart" style="left:0;top:',
                    startPosition[1] + this.$textView.getLineHeight(startLineIndex),
                    'px;width:', this.$textView.getContentWidth(),
                    'px;height:', endPosition[1] - startPosition[1] - textView.getLineHeight(endLineIndex),
                    'px"></div>');
                buff.push('<div class="QESelectionPart" style="left:0;top:', endPosition[1],
                    'px;width:', endPosition[0],
                    'px;height:', this.$textView.getLineHeight(startLineIndex),
                    'px"></div>');
            }
        }
        this.elm().innerHTML = buff.join('');
    },
    _onTextChanged: function (evt){
        // todo: 用一个positionUpdater统一这些更新offset的事情.
        var offset = evt.offset;
        var length = evt.oldText.length;
        var newLength = evt.newText.length;
        if (this.$focusOffset > offset) {
            if (this.$focusOffset < offset + length) {
                this.$focusOffset = offset;
            } else {
                this.$focusOffset += newLength - length;
            }
        }
        if (this.$anchorOffset > offset) {
            if (this.$anchorOffset < offset + length) {
                this.$anchorOffset = offset;
            } else {
                this.$anchorOffset += newLength - length;
            }
        }
        this._selectionChange();
    },
    move: function (kind, offset, shift){
        var targetOffset = this.$mover.move(kind, offset);
        if (shift) {
            this.setFocusOffset(targetOffset);
        } else {
            this.setOffset(targetOffset);
        }
    }
};

Utils.extend(Selection, Eventable);
Utils.extend(Selection, Renderable);
